/**
 * XMLMod
 * This utility is designed to allow modification of an xml file 
 * via an Ant call.  See the documentation for usage examples
 */
package com.xmlmod.util;

import java.util.HashMap;
import java.util.StringTokenizer;
import java.util.Vector;

import org.apache.tools.ant.Task;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.xmlmod.tasks.XMLMod;

/**
 * @author W. Cowger
 *
 */
public class XTrace {
	static XMLModResource resource;
	static int logLevel;
	
	private static final String PATH_SUB_ELEMENT_DELIMETER = ",";
	
	static{
		resource = XMLModResource.getResource("com.xmlmod.util.resource.XTraceResource");
		logLevel = 3;
	}
	
	public static Vector<Node> tracePath(Document doc, String path, Task task){
		Vector<Node> return_objects = new Vector<Node>();
		logLevel = ((XMLMod)task).getLogLevel();
		
		if(path != null && !("".equals(path))){
			if(path.startsWith("/")){
				task.log(resource.getMessage("XTRACE_VALID_PATH_PROCESSING", path), logLevel);
				Node document = doc.getDocumentElement();
				path = path.substring(1);
				StringTokenizer pathElements = new StringTokenizer(path, "/");
				Vector<Node> docCollection = new Vector<Node>();
				docCollection.add(document);
				processPath(docCollection, pathElements, return_objects, task);
			}else{
				task.log(resource.getMessage("XTRACE_NO_DOC_ROOT_ERROR"));
			}
		}else{
			task.log(resource.getMessage("XTRACE_READ_ERROR"));
		}
		
		return return_objects;
	}

	private static void processPath(Vector<Node> collection, StringTokenizer pathElements, Vector<Node> return_objects, Task task) {
		// TODO Auto-generated method stub
		String pathElement = pathElements.nextToken();
		String tagName = "";
		Vector<String> subPath = new Vector<String>();
		Vector<Node> children = new Vector<Node>();
		
		if(pathElement.contains("[")){
			tagName = pathElement.substring(0, pathElement.indexOf("["));
			StringTokenizer subElements = new StringTokenizer(pathElement.substring(pathElement.indexOf("[") + 1, pathElement.indexOf("]")), PATH_SUB_ELEMENT_DELIMETER);
			while(subElements.hasMoreTokens()){
				subPath.add(subElements.nextToken());
			}
		}else{
			tagName = pathElement;
		}
		 
		task.log(resource.getMessage("XTRACE_NEXT_ELEMENT", tagName + " " + subPath.toString()), logLevel);
		
		boolean breakFlag = false;
		
		for(int i=0; i<collection.size(); i++){
			Node child = collection.get(i);
			task.log("Processing child node: " + child.getNodeName(), logLevel);
			if(child.getNodeName().equals(tagName) && validateSubPath(child, subPath, task)){
				if(pathElements.hasMoreTokens()){
					if(child.hasChildNodes()){
						NodeList nodeChildren = child.getChildNodes();
						for(int j=0; j<nodeChildren.getLength(); j++){
							Node child2 = nodeChildren.item(j);
							short type = child2.getNodeType();
							if((type == Node.DOCUMENT_NODE) || type == Node.ELEMENT_NODE){
								task.log("Adding child to children group", logLevel);
								children.add(child2);
							}
						}
					}
				}else{
					return_objects.add(child);
					breakFlag = true;
				}
			}
		}
		
		if(!breakFlag && pathElements.hasMoreTokens()){
			processPath(children, pathElements, return_objects, task);
		}
		
		
	}

	private static boolean validateSubPath(Node child, Vector<String> subPath, Task task) {
		// TODO Auto-generated method stub
		// Start with a valid node and fail it if any condition is not met
		boolean returnFlag = true;
		
		// Only need to check if there are 1 or more attributes to look at
		if(subPath.size() > 0){
			NamedNodeMap attMap = child.getAttributes();
			HashMap<String, String> atts = new HashMap<String, String>();  
			
			// Create a hashMap of all attributes of this node for easier checking
			for(int j=0; j<attMap.getLength(); j++){
				Node att = attMap.item(j);
				String attName = att.getNodeName();
				String attValue = att.getNodeValue();
				
				task.log(resource.getMessage("XTRACE_INSERT_ATT_MAP", attName, attValue), logLevel);
				atts.put(attName, attValue);
			}
			
			// Check each verification condition against what is on the node
			for(int i=0; i<subPath.size(); i++){
				String da = subPath.get(i);
				String daName = da.substring(0, da.indexOf("="));
				String daValue = da.substring(da.indexOf("'") + 1, da.lastIndexOf("'"));
				task.log(resource.getMessage("XTRACE_ATT_CHECK", daName, daValue), logLevel);
			
				// Only if the condition name and value match an attribute on the node will the verification pass this cycle
				if(atts.containsKey(daName)){
					if(!atts.get(daName).equals(daValue)){
						returnFlag = false;
						break;
					}
				}else{
					returnFlag = false;
					break;
				}
			}
		}
		
		return returnFlag;
	}
}
